package com.ai.service;

import com.ai.entity.UploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

@Service
@Slf4j
public class FileUploadService {

    @Value("${app.upload.dir:./uploads}")
    private String uploadDir;

    @Value("${app.chunk.dir:./chunks}")
    private String chunkDir;

    @Value("${app.max-chunk-size:5242880}")
    private int maxChunkSize;

    /**
     * 上传文件分片
     */
    public UploadResponse uploadChunk(MultipartFile chunk, String hash, Integer index, String fileHash, Integer chunkSize, String fileName) {
        try {
            // 验证分片大小
            if (chunk.getSize() > maxChunkSize) {
                return UploadResponse.error("分片大小超过限制,每个分片最大：" + maxChunkSize + "kb");
            }

            // 确保文件哈希不为空且有效
            if (fileHash == null || fileHash.trim().isEmpty()) {
                return UploadResponse.error("文件哈希无效");
            }

            // 创建分片存储目录 - 使用绝对路径
            String absoluteChunkDir = getAbsolutePath(chunkDir);
            Path chunkPath = Paths.get(absoluteChunkDir, fileHash);
            System.out.println("分片保存路径: " + chunkPath);
            if (!Files.exists(chunkPath)) {
                Files.createDirectories(chunkPath);
                System.out.println("创建分片目录: " + chunkPath);
            }

            // 保存分片文件
            Path chunkFile = chunkPath.resolve(hash);
            System.out.println("分片文件路径: " + chunkFile);

            // 确保父目录存在
            if (!Files.exists(chunkFile.getParent())) {
                Files.createDirectories(chunkFile.getParent());
            }

            chunk.transferTo(chunkFile.toFile());
            log.info("分片保存成功: {}, 大小：{} bytes", chunkFile, chunk.getSize());

            return UploadResponse.success("分片上传成功", null, null, null);
        } catch (IOException e) {
            log.error("分片保存失败: {}", e.getMessage(), e);
            return UploadResponse.error("分片保存失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("上传失败: {}", e.getMessage(), e);
            return UploadResponse.error("上传失败: " + e.getMessage());
        }
    }

    /**
     * 合并文件分片
     *
     * 企业级断点续传方案：
     * 1、数据库存储文件元数据（推荐），将文件元数据保存db中，上传时查询是否存在，存在则不再上传
     * 2、文件名命名策略： 文件哈希 + 扩展名 (最常用)、时间分区 + 文件哈希、业务标识 + 文件哈希 (推荐)、保留原始文件名信息 + 文件哈希 (平衡方案)
     */
    public UploadResponse mergeChunks(String fileHash, String fileName, Integer totalChunks, Integer chunkSize) {
        try {
            String absoluteChunkDir = getAbsolutePath(chunkDir);
            String absoluteUploadDir = getAbsolutePath(uploadDir);

            // 完整分片目录
            Path chunkDirPath = Paths.get(absoluteChunkDir, fileHash);
            log.info("分片目录: {}", chunkDirPath);

            if (!Files.exists(chunkDirPath)) {
                return UploadResponse.error("分片目录不存在: " + chunkDirPath);
            }

            // 获取所有分片文件
            File[] chunkFiles = chunkDirPath.toFile().listFiles();
            if (chunkFiles == null) {
                return UploadResponse.error("无法读取分片目录");
            }

            log.info("找到分片数量: {}, 期望数量: {}", chunkFiles.length, totalChunks);

            if (chunkFiles.length != totalChunks) {
                return UploadResponse.error("分片数量不匹配，期望: " + totalChunks + ", 实际: " + chunkFiles.length);
            }

            // 按分片索引排序
            Arrays.sort(chunkFiles, (f1, f2) -> {
                try {
                    String name1 = f1.getName();
                    String name2 = f2.getName();

                    // 分片名称格式: fileHash-index
                    int index1 = Integer.parseInt(name1.substring(name1.lastIndexOf("-") + 1));
                    int index2 = Integer.parseInt(name2.substring(name2.lastIndexOf("-") + 1));

                    return Integer.compare(index1, index2);
                } catch (Exception e) {
                    return f1.getName().compareTo(f2.getName());
                }
            });

            // 创建上传目录
            Path uploadPath = Paths.get(absoluteUploadDir);
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
                log.info("创建上传目录: {}", uploadPath);
            }

            // 处理文件名
            //String safeFileName = generateSafeFileName(fileName);
            String safeFileName = generateEnterpriseFileName(fileName, fileHash);
            Path outputFile = uploadPath.resolve(safeFileName);

            log.info("合并后文件路径: {}", outputFile);

            // 合并分片
            try (FileOutputStream fos = new FileOutputStream(outputFile.toFile())) {
                for (File chunkFile : chunkFiles) {
                    System.out.println("合并分片: " + chunkFile.getName() + ", 大小: " + chunkFile.length());
                    Files.copy(chunkFile.toPath(), fos);
                }
            }

            // 获取文件大小
            long fileSize = Files.size(outputFile);
            log.info("文件合并完成，总大小: {} bytes", fileSize);

            // 清理分片目录
            //deleteDirectory(chunkDirPath.toFile());
            log.info("分片目录已清理: {}", chunkDirPath);

            // 生成访问URL
            String fileUrl = "/uploads/" + safeFileName;

            return UploadResponse.success("文件合并成功", fileUrl, fileSize, safeFileName);
        } catch (IOException e) {
            log.error("文件合并失败: {}", e.getMessage(), e);
            return UploadResponse.error("文件合并失败: " + e.getMessage());
        } catch (Exception e) {
            log.error("合并过程出错: {}", e.getMessage(), e);
            return UploadResponse.error("合并过程出错: " + e.getMessage());
        }
    }


    /**
     * 修复：检查文件状态 - 支持断点续传
     * 返回结构：
     * - 如果文件已存在：直接返回文件信息
     * - 如果存在未完成上传：返回已上传分片和缺失分片
     * - 如果完全不存在：返回可新上传
     */
    public UploadResponse checkFileStatus(String fileHash, String fileName, Integer totalChunks) {
        try {
            String absoluteUploadDir = getAbsolutePath(uploadDir);
            String absoluteChunkDir = getAbsolutePath(chunkDir);

            // 检查完整文件是否存在
            String storedFileName = generateEnterpriseFileName(fileName, fileHash);
            Path fullFilePath = Paths.get(absoluteUploadDir, storedFileName);

            if (Files.exists(fullFilePath)) {
                long fileSize = Files.size(fullFilePath);
                log.info("文件已存在: {}, 大小: {}", storedFileName, fileSize);
                return UploadResponse.success("文件已存在",
                        "/uploads/" + storedFileName, fileSize, storedFileName);
            }

            // 检查分片目录
            Path chunkDirPath = Paths.get(absoluteChunkDir, fileHash);
            log.info("检查分片目录: {}", chunkDirPath);

            if (Files.exists(chunkDirPath)) {
                File[] chunkFiles = chunkDirPath.toFile().listFiles();
                if (chunkFiles != null && chunkFiles.length > 0) {
                    // 解析已上传的分片索引
                    Set<Integer> uploadedChunkSet = new HashSet<>();
                    for (File chunkFile : chunkFiles) {
                        try {
                            String name = chunkFile.getName();
                            // 分片命名格式: {fileHash}-{index}
                            if (name.startsWith(fileHash + "-")) {
                                int index = Integer.parseInt(name.substring(name.lastIndexOf("-") + 1));
                                uploadedChunkSet.add(index);
                            }
                        } catch (Exception e) {
                            log.warn("解析分片文件名失败: {}", chunkFile.getName());
                        }
                    }

                    List<Integer> uploadedChunks = new ArrayList<>(uploadedChunkSet);
                    Collections.sort(uploadedChunks);

                    // 计算缺失的分片
                    List<Integer> missingChunks = new ArrayList<>();
                    if (totalChunks != null && totalChunks > 0) {
                        for (int i = 0; i < totalChunks; i++) {
                            if (!uploadedChunkSet.contains(i)) {
                                missingChunks.add(i);
                            }
                        }
                    }

                    // 计算已上传的总大小
                    long uploadedSize = Arrays.stream(chunkFiles).mapToLong(File::length).sum();

                    log.info("找到已上传分片: {}/{}, 缺失分片: {}, 已上传大小: {} bytes",
                            uploadedChunks.size(), totalChunks, missingChunks.size(), uploadedSize);

                    // 返回详细的断点续传信息
                    UploadResponse response = UploadResponse.success("存在未完成的上传", null, null, null);
                    response.setUploadedChunks(uploadedChunks);
                    response.setMissingChunks(missingChunks);
                    response.setTotalChunks(totalChunks);
                    response.setUploadedSize(uploadedSize);
                    response.setIsResumable(true);

                    if (Objects.equals(uploadedChunks.size(), totalChunks)) {
                        response.setMessage("所有分片已上传完成，可进行合并操作");
                    } else {
                        response.setMessage("存在未完成的上传");
                    }
                    return response;
                }
            }

            log.info("文件不存在，可以开始新上传");
            UploadResponse response = UploadResponse.success("文件不存在", null, null, null);
            return response;
        } catch (Exception e) {
            log.error("检查文件状态出错: {}", e.getMessage(), e);
            return UploadResponse.error("检查文件状态失败: " + e.getMessage());
        }
    }

    /**
     * 生成安全的文件名
     * 每次生成的都不一样
     */
    private String generateSafeFileName(String originalFileName) {
        if (originalFileName == null || originalFileName.isEmpty()) {
            return "unknown_file_" + System.currentTimeMillis();
        }

        // 提取文件扩展名
        String extension = "";
        int dotIndex = originalFileName.lastIndexOf(".");
        if (dotIndex > 0 && dotIndex < originalFileName.length() - 1) {
            extension = originalFileName.substring(dotIndex);
        }

        // 生成基础名称
        String baseName = originalFileName.substring(0, dotIndex > 0 ? dotIndex : originalFileName.length());

        // 移除不安全字符，只保留字母、数字、中文、下划线、连字符和点
        String safeBaseName = baseName.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5_\\-.]", "_");

        // 如果名称过长，截断
        if (safeBaseName.length() > 100) {
            safeBaseName = safeBaseName.substring(0, 100);
        }

        // 添加时间戳和随机数防止重名
        String timestamp = String.valueOf(System.currentTimeMillis());
        String random = UUID.randomUUID().toString().substring(0, 8);

        String finalName = safeBaseName + "_" + timestamp + "_" + random + extension;
        log.info("文件名处理: {} -> {}", originalFileName, finalName);

        return finalName;
    }

    /**
     * 递归删除目录
     */
    private void deleteDirectory(File dir) {
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            if (files != null) {
                for (File file : files) {
                    deleteDirectory(file);
                }
            }
        }

        boolean deleted = dir.delete();
        if (deleted) {
            System.out.println("删除文件/目录: " + dir.getAbsolutePath());
        } else {
            System.err.println("删除失败: " + dir.getAbsolutePath());
        }
    }

    /**
     * 清理过期的分片文件（可选，可以定时执行）
     */
    public void cleanupExpiredChunks() {
        try {
            File chunksRoot = new File(chunkDir);
            if (!chunksRoot.exists()) return;

            File[] chunkDirs = chunksRoot.listFiles();
            if (chunkDirs == null) return;

            long now = System.currentTimeMillis();
            long expireTime = 24 * 60 * 60 * 1000; // 24小时

            for (File chunkDir : chunkDirs) {
                if (chunkDir.isDirectory()) {
                    long lastModified = chunkDir.lastModified();
                    if (now - lastModified > expireTime) {
                        deleteDirectory(chunkDir);
                    }
                }
            }
        } catch (Exception e) {
            log.error("清理过期分片文件出错: {}", e.getMessage(), e);
        }
    }

    /**
     * 获取绝对路径
     */
    private String getAbsolutePath(String path) {
        try {
            // 处理相对路径，转换为绝对路径
            if (path.startsWith("./") || path.startsWith(".\\")) {
                // 获取项目根目录
                String projectRoot = System.getProperty("user.dir");
                return Paths.get(projectRoot, path.substring(2)).toAbsolutePath().toString();
            } else if (!Paths.get(path).isAbsolute()) {
                // 如果不是绝对路径，转换为绝对路径
                return Paths.get(path).toAbsolutePath().toString();
            }
            return path;
        } catch (Exception e) {
            System.err.println("路径处理错误: " + path + ", 错误: " + e.getMessage());
            return path; // 返回原路径作为备用
        }
    }


    /**
     * 生成唯一名称（无原始文件名时使用）
     */
    private String generateUniqueName(String fileHash) {
        // 使用文件哈希前8位 + 随机数，确保唯一性
        String shortHash = fileHash.length() > 8 ? fileHash.substring(0, 8) : fileHash;
        String random = UUID.randomUUID().toString().substring(0, 4);
        return "file_" + shortHash + "_" + random;
    }

    /**
     * 企业级文件名生成策略
     */
    private String generateEnterpriseFileName(String originalFileName, String fileHash) {
        if (originalFileName == null || originalFileName.isEmpty()) {
            return generateUniqueName(fileHash);
        }

        // 提取扩展名
        String extension = "";
        int dotIndex = originalFileName.lastIndexOf(".");
        if (dotIndex > 0) {
            extension = originalFileName.substring(dotIndex);
        }

        // 方案A: 文件哈希 + 扩展名 (最常用)
        // 优点：唯一性保证，防重名，便于缓存
        //String fileNameA = fileHash + extension;

        // 方案B: 时间分区 + 文件哈希
        // 优点：文件分布均匀，便于备份和迁移
        //SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        //String datePath = sdf.format(new Date());
        //String fileNameB = datePath + "/" + fileHash + extension;

        // 方案C: 业务标识 + 文件哈希 (推荐)
        // 优点：便于业务管理，文件分类清晰
        //String businessType = "user_upload"; // 根据业务动态获取
        //String fileNameC = businessType + "/" + fileHash + extension;

        // 方案D: 保留原始文件名信息 + 文件哈希 (平衡方案)
        String baseName = originalFileName.substring(0, dotIndex > 0 ? dotIndex : originalFileName.length());
        String safeBaseName = baseName.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5_\\-.]", "_");
        if (safeBaseName.length() > 50) {
            safeBaseName = safeBaseName.substring(0, 50);
        }
        String fileNameD = safeBaseName + "_" + fileHash + extension;

        return fileNameD; // 根据需求选择合适方案，这里使用方案D
    }
}